/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package controller;

import exception.MyException;
import exception.UnifiedExceptionConsole;
import gnu.io.*;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.*;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import model.CpdetectorUtils;
import model.SerialOpenAndClose;
import model.SerialPortUtil;
import org.apache.log4j.PropertyConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.*;
import util.completable_future.GetFileContentSupplier;
import util.thread_pool.GetFileContentTask;
import util.thread_pool.MyThreadPoolExecutor;
import util.thread_pool.SendFileTask;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * FXML Controller class
 *
 * @author wxhntmy
 */
public class HomePageViewController implements Initializable {

    /**
     * 开启时间戳
     */
    @FXML
    public RadioMenuItem dateFormat;

    @FXML
    public ProgressBar sendFileProgressBar;
    /**
     * 菜单栏
     */
    @FXML
    private MenuBar menubar;
    /**
     * 清除窗口按键
     */
    @FXML
    private Button btClearWindow;
    /**
     * 打开文件按键
     */
    @FXML
    private Button btOpenFile;
    /**
     * 显示区
     */
    @FXML
    private TextArea displayTextArea;
    /**
     * 显示打开的文件地址
     */
    @FXML
    private TextField fileTextField;
    /**
     * 发送文件按键
     */
    @FXML
    private Button btSendFile;
    /**
     * 小的发送按键
     */
    @FXML
    private Button btSend;
    /**
     * 清空发送区按键
     */
    @FXML
    private Button btClearSendBuff;
    /**
     * 串口列表
     */
    @FXML
    private ComboBox<String> comList;
    /**
     * 打开串口按键
     */
    @FXML
    private Button btOpenSerial;
    /**
     * 大的发送按键
     */
    @FXML
    private Button btOpenSend;
    /**
     * 发送区
     */
    @FXML
    private TextArea sendTextArea;
    /**
     * 波特率列表
     */
    @FXML
    private ComboBox<Integer> comBaudRate;
    /**
     * 数据位列表
     */
    @FXML
    private ComboBox<Integer> comDataBits;
    /**
     * 停止位列表
     */
    @FXML
    private ComboBox<String> comStopBits;
    /**
     * 校验方式列表
     */
    @FXML
    private ComboBox<String> comParity;
    /**
     * 发送数据长度显示
     */
    @FXML
    private Label sendDataLength;
    /**
     * 接收数据长度显示
     */
    @FXML
    private Label receiveDataLength;
    /**
     * 串口配置显示
     */
    @FXML
    private Label serialUtilDisplay;
    /**
     * 加回车换行
     */
    @FXML
    private CheckBox lineFeedCheckBox;
    /**
     * 显示缓冲上限200K
     */
    @FXML
    private RadioMenuItem displayBufferUpperLimit200K;
    /**
     * 显示缓冲上限500K
     */
    @FXML
    private RadioMenuItem displayBufferUpperLimit500K;
    /**
     * 显示缓冲上限1M
     */
    @FXML
    private RadioMenuItem displayBufferUpperLimit1M;
    /**
     * 显示缓冲上限2M
     */
    @FXML
    private RadioMenuItem displayBufferUpperLimit2M;
    /**
     * 显示缓冲上限5M
     */
    @FXML
    private RadioMenuItem displayBufferUpperLimit5M;
    /**
     * 回车后发送
     */
    @FXML
    private RadioMenuItem enterSend;
    /**
     * 发送按键键值
     */
    @FXML
    private RadioMenuItem sendButtonValue;
    /**
     * 本地回显按键键值
     */
    @FXML
    private RadioMenuItem displayButtonValue;
    /**
     * 发送回车0x0a后自动附加换行符0x0d
     */
    @FXML
    private RadioMenuItem sendEnterAndNewlineCharacter;
    /**
     * 刷新串口列表
     */
    @FXML
    private MenuItem refreshSerialPortList;
    /**
     * 设置字体/颜色/大小
     */
    @FXML
    private MenuItem fontColorSize;
    /**
     * 背景颜色
     */
    @FXML
    private MenuItem backgroundColor;
    /**
     * 显示缓冲上限帮助
     */
    @FXML
    private MenuItem displayBufferUpperLimitHelp;
    /**
     * 发送帮助
     */
    @FXML
    private MenuItem sendHelp;
    /**
     * 多字符串
     */
    @FXML
    private Menu multipleStrings;
    /**
     * 小工具
     */
    @FXML
    private Menu gadget;
    /**
     * 帮助
     */
    @FXML
    private MenuItem help;
    /**
     * 联系作者
     */
    @FXML
    private MenuItem contactAuthor;

    /**
     * 显示缓冲上限单选框
     */
    ToggleGroup displayBufferUpperLimitGroup = new ToggleGroup();

    /**
     * 串口列表
     */
    private List<String> portList = null;
    /**
     * 串口参数
     */
    private SerialOpenAndClose parameter = new SerialOpenAndClose();
    /**
     * 串口对象
     */
    private SerialPort serialPort = null;
    /**
     * 已发送的数据长度
     */
    private Integer sendLength = 0;
    /**
     * 已接收的数据长度
     */
    private Integer receiveLength = 0;
    /**
     * 默认串口名字
     */
    private String serialPortName = "COM1";
    /**
     * 默认串口状态
     */
    private String serialPortStatus = "已关闭";

    /**
     * f为选择到的文件
     */
    private File f = null;

    /**
     * 默认显示缓冲上限200K
     */
    private long handledisplayBufferUpperLimit = 1024 * 200;

    /**
     * 读取配置文件
     */
    private LoadConfigInfo loadConfigInfo = new LoadConfigInfo();

    /**
     * Log4j日志打印工厂
     */
    private final static Logger logger = LoggerFactory.getLogger(HomePageViewController.class);

    private final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");

    /**
     * 线程池
     */
    private final MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor();

    /**
     * 线程池实例
     */
    private final ExecutorService executorService = myThreadPoolExecutor.createNewThreadPool();

    /**
     * 初始化控制类
     *
     * @param url 无
     * @param rb  无
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO

        //隐藏发送文件进度条
        sendFileProgressBar.setVisible(false);

        //加入combobox列表
        comBaudRate.getItems().addAll(loadConfigInfo.getBaudRate());
        comDataBits.getItems().addAll(loadConfigInfo.getDataBits());
        comStopBits.getItems().addAll(loadConfigInfo.getStopBits());
        comParity.getItems().addAll(loadConfigInfo.getParity());
        if (getSystemPortList()) {
            for (String s : portList) {
                comList.getItems().add(s);
            }
        }

        //将单选框加入到同一个组里，同一个组里的单选键只能被选中一个
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit200K);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit500K);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit1M);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit2M);
        displayBufferUpperLimitGroup.getToggles().add(displayBufferUpperLimit5M);

        //设置显示区自动换行
        displayTextArea.setWrapText(true);

        dateFormat.setSelected(loadConfigInfo.isDateFormat());

        //设置串口初始值，防止空指针
        comBaudRate.setValue(loadConfigInfo.getComBaudRate());
        comDataBits.setValue(loadConfigInfo.getComDataBits());
        comStopBits.setValue(loadConfigInfo.getComStopBits());
        comParity.setValue(loadConfigInfo.getComParity());
        comList.setValue(loadConfigInfo.getSerialPortName());
        //显示初始接收数据长度
        receiveDataLength.setText("Receive: " + receiveLength);
        //显示初始发送数据长度
        sendDataLength.setText("Send: " + sendLength);
        //显示串口初始值
        serialUtilDisplay.setText(serialPortName + "   " + serialPortStatus + "   " + comBaudRate.getValue()
                + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());

        //默认按Ctrl+Enter发送
        enterSend.setSelected(loadConfigInfo.isEnterSend());
        sendButtonValue.setSelected(loadConfigInfo.isSendButtonValue());
        displayButtonValue.setSelected(loadConfigInfo.isDisplayButtonValue());
        sendEnterAndNewlineCharacter.setSelected(loadConfigInfo.isSendEnterAndNewlineCharacter());
        lineFeedCheckBox.setSelected(loadConfigInfo.isLineFeedCheckBox());

        displayBufferUpperLimit200K.setSelected(loadConfigInfo.isDisplayBufferUpperLimit200K());
        displayBufferUpperLimit500K.setSelected(loadConfigInfo.isDisplayBufferUpperLimit500K());
        displayBufferUpperLimit1M.setSelected(loadConfigInfo.isDisplayBufferUpperLimit1M());
        displayBufferUpperLimit2M.setSelected(loadConfigInfo.isDisplayBufferUpperLimit2M());
        displayBufferUpperLimit5M.setSelected(loadConfigInfo.isDisplayBufferUpperLimit5M());

        if (displayBufferUpperLimit200K.isSelected()) {
            handledisplayBufferUpperLimit = 1024 * 200;
        }
        if (displayBufferUpperLimit500K.isSelected()) {
            handledisplayBufferUpperLimit = 1024 * 500;
        }
        if (displayBufferUpperLimit1M.isSelected()) {
            handledisplayBufferUpperLimit = 1024 * 1024;
        }
        if (displayBufferUpperLimit2M.isSelected()) {
            handledisplayBufferUpperLimit = 1024 * 1024 * 2;
        }
        if (displayBufferUpperLimit5M.isSelected()) {
            handledisplayBufferUpperLimit = 1024 * 1024 * 5;
        }

        //设置字体和字体大小
        displayTextArea.setFont(Font.font(loadConfigInfo.getFont(), FontWeight.NORMAL, Integer.parseInt(loadConfigInfo.getFontSize())));
        //设置字体颜色
        displayTextArea.setStyle("-fx-text-fill: #" + loadConfigInfo.getFontColor().substring(2));
    }

    /**
     * 大的发送按键单击事件
     *
     * @param event 按键单击事件
     */
    @FXML
    private void handlebtOpenSendAction(ActionEvent event) {

        //打开了串口才能发送
        if ("关闭串口".equals(btOpenSerial.getText())) {
            String s = sendTextArea.getText();
            //发送区不为空才能发送
            if (!s.isEmpty()) {
                //选中了加换行符发送
                if (lineFeedCheckBox.isSelected()) {
                    s = s + "\r\n";
                }
                //选中了发送回车0x0a后自动附加换行符0x0d
                if (sendEnterAndNewlineCharacter.isSelected()) {
                    char lastChar = s.charAt(s.length() - 1);
                    if ((char) 0x0D == lastChar) {
                        s = s + 0x0A;
                    }
                }
                sendData(s);
            } else {
                Common.alert(AlertType.WARNING, "警告", "发送内容不能为空！");
            }
        } else {
            Common.alert(AlertType.WARNING, "警告", "串口还没有打开！");
        }
    }

    /**
     * 发送数据并更新显示区
     * 此方法默认发送字符编码格式为GBK
     *
     * @param data 需要发送的数据
     */
    public void sendData(String data) {
        //把字符串转为二进制数组
        byte[] bytes = new byte[0];
        try {
            bytes = data.getBytes(loadConfigInfo.getSerialPortSendDataCharsetName());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        //记录发送的数据长度
        sendLength = sendLength + bytes.length;
        //发送数据
        SerialPortUtil.sendData(serialPort, bytes, loadConfigInfo.getSerialPortSendDataCharsetName());
        if (displayTextArea.getText().length() < handledisplayBufferUpperLimit) {
            if (dateFormat.isSelected()) {
                displayTextArea.appendText("[" + simpleDateFormat.format(new Date()) + "] 发→：" + data + "\r\n");
            } else {
                displayTextArea.appendText(data + "\r\n");
            }
            //显示更新的发送的数据长度
            sendDataLength.setText("Send: " + sendLength);
        } else {
            displayTextArea.appendText("已达显示区缓冲上限！\n");
        }
    }

    /**
     * 设置串口接收监听事件
     */
    public void getSerialPortActionEvent() {
        try {
            SerialPortUtil.setListenerToSerialPort(serialPort, events -> {
                try {
                    //logger.info("串口接收监听事件: {}", events.getEventType());
                    //数据通知
                    if (events.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                        //读串口数据
                        byte[] bytess = SerialPortUtil.readData(serialPort, executorService);
                        if (null == bytess || bytess.length == 0) {
                            throw new MyException("串口读取的数据为空！！！");
                        }
                        //更新接收的数据长度
                        receiveLength = receiveLength + bytess.length;
                        if (displayTextArea.getText().length() < handledisplayBufferUpperLimit) {
                            //显示接收的数据
                            try {
                                if (dateFormat.isSelected()) {
                                    displayTextArea.appendText("[" + simpleDateFormat.format(new Date()) + "] 收←：" + new String(bytess, loadConfigInfo.getSerialPortReadDataCharsetName()) + "\r\n");
                                } else {
                                    displayTextArea.appendText(new String(bytess, loadConfigInfo.getSerialPortReadDataCharsetName()) + "\r\n");
                                }

                            } catch (UnsupportedEncodingException e) {
                                UnifiedExceptionConsole.console(e);
                            }
                        /*
                          Java FX程序不允许用户在FX application thread线程外更新界面的组件信息
                          即所有的更新界面组件的信息的代码，都应该在在FX application thread线程中执行。
                          用Platform类的runLater方法可以解决该问题
                          该方法可以将参数runnable将要执行的代码，交给FX application thread线程执行，这样问题就解决了
                         */
                            Platform.runLater(() -> {
                                //任何更新界面组件信息的代码
                                //显示更新的接收的数据长度
                                receiveDataLength.setText("Receive: " + receiveLength);
                            });
                        } else {
                            displayTextArea.appendText("已达显示区缓冲上限！\n");
                        }
                    }
                } catch (Exception e) {
                    Common.alert(AlertType.WARNING, "警告", e.getMessage());
                    logger.error("Exception", e);

                }

            });
        } catch (TooManyListenersException ex) {
            logger.error("TooManyListenersException", ex);
        }
    }

    /**
     * 获取串口列表
     *
     * @return 如果串口列表不为空，返回true
     */
    public boolean getSystemPortList() {
        //获取系统可用的串口列表
        portList = SerialPortUtil.getSerialPortList();
        //如果串口列表不为空，返回true
        return !portList.isEmpty();
    }

    /**
     * 清除窗口按键单击事件
     *
     * @param event 按键单击事件
     */
    @FXML
    private void handlebtClearWindowAction(ActionEvent event) {
        //清空显示区
        displayTextArea.setText("");
        //清空打开的文件的绝对地址
        //fileTextField.setText("");
        //清空接收的数据长度
        receiveLength = 0;
        //清空发送的数据长度
        sendLength = 0;
        //显示新的接收的数据长度
        receiveDataLength.setText("Receive: " + receiveLength);
        //显示新的发送的数据长度
        sendDataLength.setText("Send: " + sendLength);
    }

    /**
     * 打开文件按键单击事件
     *
     * @param event 按键单击事件
     */
    @FXML
    private void handlebtOpenFileAction(ActionEvent event) {
        //创建FileChooser需要一个新的Stage
        Stage stage = new Stage();
        //新建FileChooser选择文件
        FileChooser fileChooser = new FileChooser();
        //打开上一次打开的文件夹
        if (!"".equals(loadConfigInfo.getLastFilePath())) {
            fileChooser.setInitialDirectory(new File(loadConfigInfo.getLastFilePath()));
        }
        fileChooser.setTitle("打开文件");
        fileChooser.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("All Files", "*.*"));
        f = fileChooser.showOpenDialog(stage);

        //选择到的文件不为空
        if (null != f) {

            loadConfigInfo.setLastFilePath(f.getParent());
            logger.info("LastFilePath: {}", loadConfigInfo.getLastFilePath());

            //显示选择到的文件的绝对地址
            fileTextField.setText(f.getAbsolutePath());

            //方法一
            GetFileContentTask getFileContentTask = new GetFileContentTask("GetFileContentTask001", displayTextArea, handledisplayBufferUpperLimit, f);
            executorService.submit(getFileContentTask);

            //方法二
            /*GetFileContentSupplier getFileContentSupplier = new GetFileContentSupplier(displayTextArea, handledisplayBufferUpperLimit, f);
            CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(getFileContentSupplier
                            , executorService)
                    .whenComplete((result, e) -> {
                        //执行线程执行完以后的操作。
                        //System.out.println(result + " " + e);
                        logger.info("打开文件异步线程执行完成！");
                        logger.info("打开文件异步线程返回：{}", result);
                    }).exceptionally((e) -> {
                        //抛出异常
                        e.printStackTrace();
                        //System.out.println("exception " + e);
                        return "Exception";
                    });*/

        } else {
            Common.alert(AlertType.WARNING, "警告", "未选择文件！");
        }

    }


    /**
     * 发送文件按键单击事件
     *
     * @param event 按键单击事件
     */
    @FXML
    private void handlebtSendFileAction(ActionEvent event) {

        //打开了串口才能发送
        if ("关闭串口".equals(btOpenSerial.getText())) {
            File file = new File(fileTextField.getText());
            //文件内容的读取
            if (file.exists()) {
                String charsetName = CpdetectorUtils.getFileCharset(f.getAbsolutePath());
                logger.info("Open File CharsetName: {}", charsetName);
                logger.info("Send File CharsetName: {}", loadConfigInfo.getSerialPortSendDataCharsetName());

                displayTextArea.setText("");
                displayTextArea.appendText("文件大小: " + f.length() + "\n");
                displayTextArea.appendText("波特率 " + parameter.getBaudRate() + "bps\n");
                displayTextArea.appendText("请稍后...\n");

                //显示进度条
                sendFileProgressBar.setVisible(true);

                //方法一：绑定进度条的后台线程
                SendFileTask sendFileTask = new SendFileTask("SendFileTask001", f, serialPort, sendLength, sendDataLength, displayTextArea);
                sendFileProgressBar.progressProperty().bind(sendFileTask.progressProperty());
                executorService.submit(sendFileTask);
                //线程任务执行成功回调
                sendFileTask.setOnSucceeded(t -> {
                    //文件发送完成，关闭进度条
                    sendFileProgressBar.setVisible(false);
                    //获取Task返回的值
                    sendLength = (Integer) t.getSource().getValue();
                    logger.info("SendFileTask001 done! Get Return Value :" + t.getSource().getValue());
                });

                //方法二：异步线程执行
                /*Long t1 = System.currentTimeMillis();
                CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(
                                new SendFileSupplier(f, serialPort, sendLength, sendDataLength), executorService)
                        .whenComplete((result, e) -> {
                            //执行线程执行完以后的操作。
                            //System.out.println(result + " " + e);
                            sendLength = (Integer) result;
                            Long t2 = System.currentTimeMillis();
                            Platform.runLater(() -> {
                                displayTextArea.appendText("发送耗时：" + (t2 - t1) + " ms\n");
                            });
                            logger.info("发送文件异步线程执行完成！");
                            logger.info("发送文件异步线程执行耗时：{} ms", (t2 - t1));
                        }).exceptionally((e) -> {
                            //抛出异常
                            e.printStackTrace();
                            //System.out.println("exception " + e);
                            return 0;
                        });
                */


                displayTextArea.appendText("发送完毕！\n");

            }
            else {
                Common.alert(AlertType.WARNING, "警告", "还没有选择文件！");
            }
        } else {
            Common.alert(AlertType.WARNING, "警告", "串口还没有打开！");
        }
    }

    /**
     * 小的发送按键单击事件
     *
     * @param event 按键单击事件
     */
    @FXML
    private void handlebtSendAction(ActionEvent event) {
        handlebtOpenSendAction(event);
    }

    /**
     * 清除发送区按键单击事件
     *
     * @param event 按键单击事件
     */
    @FXML
    private void handlebtClearSendBuffAction(ActionEvent event) {
        sendTextArea.setText("");
    }

    /**
     * 根据ComStopBits的值设置停止位
     */
    public void setStopBits() {
        int stopBit = SerialPort.STOPBITS_1;
        switch (comStopBits.getValue()) {
            case "1":
                break;
            case "1.5":
                stopBit = SerialPort.STOPBITS_1_5;
                break;
            case "2":
                stopBit = SerialPort.STOPBITS_2;
                break;
        }
        //停止位
        parameter.setStopBits(stopBit);
    }

    /**
     * 设置校验方式
     */
    public void setParity() {
        int parity = SerialPort.PARITY_NONE;
        switch (comParity.getValue()) {
            case "None":
                break;
            case "Odd":
                parity = SerialPort.PARITY_ODD;
                break;
            case "Even":
                parity = SerialPort.PARITY_EVEN;
                break;
            case "Mark":
                parity = SerialPort.PARITY_MARK;
                break;
            case "Space":
                parity = SerialPort.PARITY_SPACE;
                break;
        }

        //校验方式
        parameter.setParity(parity);
    }

    /**
     * 打开串口按键单击事件
     *
     * @param event 按键单击事件
     * @return 串口状态。1：已打开。2：对应串口不存在。3：串口在使用中。4：不支持操作。
     */
    @FXML
    private Integer handlebtOpenSerialAction(ActionEvent event) {
        //串口名
        parameter.setSerialPortName(comList.getValue());
        //波特率
        parameter.setBaudRate(comBaudRate.getValue());
        //数据位
        parameter.setDataBits(comDataBits.getValue());

        setStopBits();
        setParity();


        int status = 0;
        /*
          打开串口字符常量
         */
        if ("打开串口".equals(btOpenSerial.getText())) {
            try {
                //打开串口
                serialPort = SerialPortUtil.openSerialPort(parameter);
                serialPortName = parameter.getSerialPortName();
                serialPortStatus = "已开启";
                serialUtilDisplay.setText(serialPortName + "   " + serialPortStatus + "   " + parameter.getBaudRate()
                        + "bps, " + parameter.getDataBits() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
                //打开串口之后开启串口监听
                getSerialPortActionEvent();
                status = 1;
            } catch (Exception e) {
                if (e instanceof NoSuchPortException) {
                    try {
                        //对应串口不存在
                        status = 2;
                        throw new MyException("对应串口不存在！");
                    } catch (MyException ex) {
                        ex.printStackTrace();
                    }
                } else if (e instanceof PortInUseException) {
                    try {
                        //串口在使用中
                        status = 3;
                        throw new MyException("当前串口在使用中！");
                    } catch (MyException ex) {
                        ex.printStackTrace();
                    }
                } else if (e instanceof UnsupportedCommOperationException) {
                    try {
                        //不支持操作
                        status = 4;
                        throw new MyException("不支持的串口操作！");
                    } catch (MyException ex) {
                        ex.printStackTrace();
                    }
                } else {
                    try {
                        throw new MyException("未定义的异常！");
                    } catch (MyException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }

        if (status == 1) {
            //设置字体颜色为红色
            btOpenSerial.setTextFill(Paint.valueOf("red"));
            //字体加粗
            btOpenSerial.setFont(Font.font("System", FontWeight.BOLD, 24));
            btOpenSerial.setText("关闭串口");
            //return返回
            return 1;
        }
        if (status == 2) {
            Common.alert(AlertType.WARNING, "警告", "串口" + parameter.getSerialPortName() + "不存在！");
            return 2;
        }
        if (status == 3) {
            Common.alert(AlertType.WARNING, "警告", "串口" + parameter.getSerialPortName() + "正在使用中！");
            return 3;
        }
        if (status == 4) {
            Common.alert(AlertType.WARNING, "警告", "串口" + parameter.getSerialPortName() + "不支持操作！");
            return 4;
        }
        if ("关闭串口".equals(btOpenSerial.getText())) {
            //设置字体颜色为黑色
            btOpenSerial.setTextFill(Paint.valueOf("black"));
            //字体粗细正常
            btOpenSerial.setFont(Font.font("System", FontWeight.NORMAL, 24));
            btOpenSerial.setText("打开串口");
            //关闭串口
            closeSerialPort();
            return 5;
        }
        return 6;
    }

    /**
     * 关闭串口
     */
    public void closeSerialPort() {
        //关闭已打开的串口
        SerialPortUtil.closeSerialPort(serialPort);
        serialPortStatus = "已关闭";
        //更新串口状态
        serialUtilDisplay.setText(serialPortName + "   " + serialPortStatus + "   " + parameter.getBaudRate()
                + "bps, " + parameter.getDataBits() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
    }

    /**
     * 串口列表combobox点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlecomListAction(ActionEvent event) {

        if (null != comList.getValue()) {
            serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
                    + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
            //设置串口名称
            parameter.setSerialPortName(comList.getValue());

            logger.info("串口列表combobox点击事件: {}", comList.getValue());
            loadConfigInfo.setSerialPortName(comList.getValue());
        }
    }

    /**
     * 波特率列表combobox点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlecomBaudRateAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
                + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        //设置波特率
        parameter.setBaudRate(comBaudRate.getValue());

        loadConfigInfo.setComBaudRate(comBaudRate.getValue());
    }

    /**
     * 数据位列表combobox点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlecomDataBitsAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
                + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        //设置数据位
        parameter.setDataBits(comDataBits.getValue());

        loadConfigInfo.setComDataBits(comDataBits.getValue());
    }

    /**
     * 停止位列表combobox点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlecomStopBitsAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
                + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        setStopBits();

        loadConfigInfo.setComStopBits(comStopBits.getValue());
    }

    /**
     * 校验方式列表combobox点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlecomParityAction(ActionEvent event) {
        serialUtilDisplay.setText(comList.getValue() + "   " + serialPortStatus + "   " + comBaudRate.getValue()
                + "bps, " + comDataBits.getValue() + ", " + comStopBits.getValue() + ", " + comParity.getValue());
        setParity();

        loadConfigInfo.setComParity(comParity.getValue());
    }

    /**
     * 菜单：更新串口列表点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlerefreshSerialPortListAction(ActionEvent event) {
        //刷新串口列表之前先清空原来的串口列表
        comList.getItems().clear();
        //获取系统可用的串口列表
        if (getSystemPortList()) {
            if (comList != null) {
                for (String s : portList) {
                    //更新串口列表
                    comList.getItems().add(s);
                }
            }
        }
        if (null == comList) {
            try {
                Common.alert(AlertType.WARNING, "警告", "系统可用的串口列表为空！！！");
                throw new MyException("系统可用的串口列表为空！！！");
            } catch (MyException e) {
                logger.error("MyException", e);
                return;
            }
        }
        comList.setValue(comList.getItems().get(0));
    }

    /**
     * 菜单：字体/颜色/大小点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlefontColorSizeAction(ActionEvent event) {
        //设置dialog的返回类型，dialog返回的是List类型.
        Dialog<ArrayList<Object>> dialog;
        dialog = new Dialog<>();
        //dialog标题
        dialog.setTitle("设置字体/颜色/大小");
        dialog.setHeaderText(null);

        //设置按键类型.
        ButtonType confirmButtonType = new ButtonType("确定", ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().addAll(confirmButtonType, ButtonType.CANCEL);

        //设置网格布局窗格GridPane
        GridPane grid = new GridPane();
        //列之间水平间隙的宽度
        grid.setHgap(10);
        //行之间垂直间隙的高度。
        grid.setVgap(10);
        //构造具有四个不同偏移的新插入实例。
        grid.setPadding(new Insets(20, 150, 10, 10));

        //字体列表
        ComboBox<String> comFont = new ComboBox<>();
        //获取当前的字体
        comFont.setValue(loadConfigInfo.getFont());
        //获取系统已安装的字体
        List<String> mylist = Font.getFamilies();
        for (String s : mylist) {
            comFont.getItems().add(s);
        }

        //颜色选择器
        ColorPicker colorPicker = new ColorPicker();
        colorPicker.setValue(Color.web(loadConfigInfo.getFontColor()));

        //字体大小列表
        ComboBox<Integer> comSize = new ComboBox<>();
        //获取当前的字体大小
        //Double fontSize = displayTextArea.getFont().getSize();
        comSize.setValue(Integer.parseInt(loadConfigInfo.getFontSize()));
        comSize.getItems().addAll(7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30);

        grid.add(new Label("字体："), 0, 0);
        grid.add(comFont, 1, 0);
        grid.add(new Label("颜色："), 0, 1);
        grid.add(colorPicker, 1, 1);
        grid.add(new Label("字体大小："), 0, 2);
        grid.add(comSize, 1, 2);

        //把网格布局窗格添加到dialog里
        dialog.getDialogPane().setContent(grid);
        //单击确定按钮时，将结果转换为List返回。
        dialog.setResultConverter(dialogButton -> {
            if (dialogButton == confirmButtonType) {
                ArrayList<Object> list = new ArrayList<>();
                list.add(comFont.getValue());
                list.add(colorPicker.getValue());
                list.add(comSize.getValue());
                return list;
            }
            return null;
        });
        //接收dialog返回的List
        Optional<ArrayList<Object>> result = dialog.showAndWait();

        logger.info("字体/颜色/大小点击事件: {}", result.toString());

        if (result.isPresent()) {
            loadConfigInfo.setFontColor(result.get().get(1).toString());
            loadConfigInfo.setFontSize(result.get().get(2).toString());
            loadConfigInfo.setFont(result.get().get(0).toString());

            //设置字体和字体大小
            displayTextArea.setFont(Font.font(loadConfigInfo.getFont(), FontWeight.NORMAL, Integer.parseInt(loadConfigInfo.getFontSize())));
            //设置字体颜色
            displayTextArea.setStyle("-fx-text-fill: #" + loadConfigInfo.getFontColor().substring(2));

        }


    }

    /**
     * 菜单：背景颜色点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlebackgroundColorAction(ActionEvent event) {
        //设置dialog的返回类型，dialog返回的是List类型.
        Dialog<ArrayList<Object>> dialog;
        dialog = new Dialog<>();
        //dialog标题
        dialog.setTitle("设置背景颜色");
        dialog.setHeaderText(null);

        //设置按键类型.
        ButtonType confirmButtonType = new ButtonType("确定", ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().addAll(confirmButtonType, ButtonType.CANCEL);

        //设置网格布局窗格GridPane
        GridPane grid = new GridPane();
        //列之间水平间隙的宽度
        grid.setHgap(10);
        //行之间垂直间隙的高度。
        grid.setVgap(10);
        //构造具有四个不同偏移的新插入实例。
        grid.setPadding(new Insets(20, 150, 10, 10));

        //颜色选择器
        ColorPicker colorPicker = new ColorPicker();
        colorPicker.setValue(Color.web(loadConfigInfo.getBackGroundColor()));

        grid.add(new Label("背景颜色："), 0, 0);
        grid.add(colorPicker, 1, 0);

        //把网格布局窗格添加到dialog里
        dialog.getDialogPane().setContent(grid);

        //单击确定按钮时，将结果转换为List返回。
        dialog.setResultConverter(dialogButton -> {
            if (dialogButton == confirmButtonType) {
                ArrayList<Object> list = new ArrayList<>();
                list.add(colorPicker.getValue());
                return list;
            }
            return null;
        });

        //接收dialog返回的List
        Optional<ArrayList<Object>> result = dialog.showAndWait();

        logger.info("背景颜色点击事件: {}", result.toString());

        if (result.isPresent()) {
            logger.info(result.toString());
            loadConfigInfo.setBackGroundColor(result.get().get(0).toString());

            //设置字体和字体大小
            displayTextArea.setFont(Font.font(loadConfigInfo.getFont(), FontWeight.NORMAL, Integer.parseInt(loadConfigInfo.getFontSize())));

            //设置背景颜色
            displayTextArea.setStyle("-fx-background-color: #" + loadConfigInfo.getBackGroundColor().substring(2));
        }

    }

    /**
     * 菜单：显示缓冲上限200K点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handledisplayBufferUpperLimit200KAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 200;
        loadConfigInfo.setDisplayBufferUpperLimit200K(displayBufferUpperLimit200K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit500K(displayBufferUpperLimit500K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit1M(displayBufferUpperLimit1M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit2M(displayBufferUpperLimit2M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit5M(displayBufferUpperLimit5M.isSelected());
    }

    /**
     * 菜单：显示缓冲上限500K点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handledisplayBufferUpperLimit500KAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 500;
        loadConfigInfo.setDisplayBufferUpperLimit200K(displayBufferUpperLimit200K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit500K(displayBufferUpperLimit500K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit1M(displayBufferUpperLimit1M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit2M(displayBufferUpperLimit2M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit5M(displayBufferUpperLimit5M.isSelected());
    }

    /**
     * 菜单：显示缓冲上限1M点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handledisplayBufferUpperLimit1MAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 1024;
        loadConfigInfo.setDisplayBufferUpperLimit200K(displayBufferUpperLimit200K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit500K(displayBufferUpperLimit500K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit1M(displayBufferUpperLimit1M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit2M(displayBufferUpperLimit2M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit5M(displayBufferUpperLimit5M.isSelected());
    }

    /**
     * 菜单：显示缓冲上限2M点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handledisplayBufferUpperLimit2MAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 1024 * 2;
        loadConfigInfo.setDisplayBufferUpperLimit200K(displayBufferUpperLimit200K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit500K(displayBufferUpperLimit500K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit1M(displayBufferUpperLimit1M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit2M(displayBufferUpperLimit2M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit5M(displayBufferUpperLimit5M.isSelected());
    }

    /**
     * 菜单：显示缓冲上限5M点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handledisplayBufferUpperLimit5MAction(ActionEvent event) {
        handledisplayBufferUpperLimit = 1024 * 1024 * 5;
        loadConfigInfo.setDisplayBufferUpperLimit200K(displayBufferUpperLimit200K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit500K(displayBufferUpperLimit500K.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit1M(displayBufferUpperLimit1M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit2M(displayBufferUpperLimit2M.isSelected());
        loadConfigInfo.setDisplayBufferUpperLimit5M(displayBufferUpperLimit5M.isSelected());
    }

    /**
     * 菜单：显示缓冲上限帮助点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handledisplayBufferUpperLimitHelpAction(ActionEvent event) {
        Common.alert(AlertType.INFORMATION, "发送帮助", "需要帮助请联系作者！");
    }

    /**
     * 菜单：回车发送点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handleenterSendAction(ActionEvent event) {
        if (sendButtonValue.isSelected() && displayButtonValue.isSelected()) {
            sendButtonValue.setSelected(false);
        }
        loadConfigInfo.setSendButtonValue(sendButtonValue.isSelected());
        loadConfigInfo.setDisplayButtonValue(displayButtonValue.isSelected());
        loadConfigInfo.setEnterSend(enterSend.isSelected());
    }

    /**
     * 输入区键值检测
     *
     * @param event 按键输入事件
     */
    @FXML
    private void getButtonValueAction(KeyEvent event) {
        //检测到回车发送
        //菜单：回车发送被选中
        if (enterSend.isSelected()) {
            //检测键值是否为回车键
            if (event.getCode() == KeyCode.ENTER) {
                //logger.info("回车后发送输入行: {}", event);
                //打开了串口才能发送
                if ("关闭串口".equals(btOpenSerial.getText())) {
                    String s = sendTextArea.getText();
                    //logger.info("最后输入的字符为：{}", s);
                    //发送区不为空才能发送
                    if (!s.isEmpty()) {
                        //选中了加换行符发送
                        if (lineFeedCheckBox.isSelected()) {
                            s = s + "\r\n";
                        }
                        sendData(s);
                    } else {
                        Common.alert(AlertType.INFORMATION, "警告", "发送内容不能为空！");
                    }
                } else {
                    Common.alert(AlertType.INFORMATION, "警告", "串口未打开！");
                }
            }
        }
        //按键立即发送该键值
        if (sendButtonValue.isSelected()) {
            KeyCode code = event.getCode();
            //logger.info("按键立即发送该键值: {}", code);
            //打开了串口才能发送
            if ("关闭串口".equals(btOpenSerial.getText())) {
                String s = code.toString();
                //选中了加换行符发送
                if (lineFeedCheckBox.isSelected()) {
                    s = s + "\r\n";
                }
                sendData(s);
            } else {
                Common.alert(AlertType.INFORMATION, "警告", "串口未打开！");
            }
        }
    }

    /**
     * 菜单：发送键值点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlesendButtonValueAction(ActionEvent event) {
        if (sendButtonValue.isSelected() && displayButtonValue.isSelected()) {
            enterSend.setSelected(false);
        }
        loadConfigInfo.setSendButtonValue(sendButtonValue.isSelected());
        loadConfigInfo.setDisplayButtonValue(displayButtonValue.isSelected());
        loadConfigInfo.setEnterSend(enterSend.isSelected());
    }

    /**
     * 菜单：本地回显键值点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handledisplayButtonValueAction(ActionEvent event) {
        if (sendButtonValue.isSelected() && displayButtonValue.isSelected()) {
            enterSend.setSelected(false);
        }
        loadConfigInfo.setSendButtonValue(sendButtonValue.isSelected());
        loadConfigInfo.setDisplayButtonValue(displayButtonValue.isSelected());
        loadConfigInfo.setEnterSend(enterSend.isSelected());
    }

    /**
     * 菜单：发送回车0x0a后自动附加换行符0x0d点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlesendEnterAndNewlineCharacterAction(ActionEvent event) {
        loadConfigInfo.setSendEnterAndNewlineCharacter(sendEnterAndNewlineCharacter.isSelected());
    }


    /**
     * 菜单：发送帮助点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlesendHelpAction(ActionEvent event) {
        Common.alert(AlertType.INFORMATION, "发送帮助", "需要帮助请联系作者！");
    }

    /**
     * 菜单：多字符串点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlemultipleStringsAction(ActionEvent event) {
    }

    /**
     * 菜单：小工具点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlegadgetAction(ActionEvent event) {
    }

    /**
     * 菜单：帮助点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlehelpAction(ActionEvent event) {
        Common.alert(AlertType.INFORMATION, "帮助", "需要帮助请联系作者！");
    }

    /**
     * 菜单：联系作者点击事件
     *
     * @param event 点击事件
     */
    @FXML
    private void handlecontactAuthorAction(ActionEvent event) {
        Common.alert(AlertType.INFORMATION, "联系作者", "邮箱：wxhntmy@163.com（非提交bug勿扰）");
    }

    /**
     * 开启时间戳
     *
     * @param actionEvent 点击事件
     */
    @FXML
    public void handledateFormat(ActionEvent actionEvent) {
        loadConfigInfo.setDateFormat(dateFormat.isSelected());
    }

    /**
     * 加回车换行
     *
     * @param actionEvent 点击事件
     */
    @FXML
    public void handlelineFeedCheckBox(ActionEvent actionEvent) {
        loadConfigInfo.setLineFeedCheckBox(lineFeedCheckBox.isSelected());
    }
}
